package v2_encode_and_decode

import (
	"context"
	"encoding/json"
	"errors"
	"go-demo/micro/rpc/v2-encode_and_decode/message"
	"net"
	"reflect"
)

// Server 服务端:
//		• 启动服务器监听端口
//		• 接收连接，并且读取数据
//		• 将数据还原回调用信息
//		• 根据服务名查找该实例上注册的服务
//		• 利用反射执行方法调用
//		• 写回响应
// 客户端和服务端是对称的
type Server struct {
	services      map[string]reflectionStub
	network, addr string
}

type reflectionStub struct {
	service Service
	value   reflect.Value // service 的反射值
}

// 反射找到 methodName 方法，并且反射调用执行该方法
func (r *reflectionStub) invoke(methodName string, data []byte) ([]byte, error) {
	// 反射找到方法，并且执行调用
	method := r.value.MethodByName(methodName)
	in := make([]reflect.Value, 2)
	in[0] = reflect.ValueOf(context.Background()) // 不知道怎么传这个 context，先直接写死
	// 反射拿到该方法的参数类型，并创建出该类型的零值值
	inReq := reflect.New(method.Type().In(1).Elem())
	err := json.Unmarshal(data, inReq.Interface())
	if err != nil {
		return nil, err
	}
	in[1] = inReq
	results := method.Call(in) // 真正的反射调用该方法
	// results[0] 是返回值
	// results[1] 是error
	if results[1].Interface() != nil {
		err = results[1].Interface().(error)
	}

	var res []byte
	if results[0].IsNil() {
		return nil, err
	} else {
		var er error
		res, er = json.Marshal(results[0].Interface())
		if er != nil {
			return nil, er
		}
	}
	return res, err
}

// Invoke 服务端的代理实现：
// 		将数据还原回调用信息;根据服务名查找该实例上注册的服务;利用反射执行方法调用;写回响应
func (s *Server) Invoke(ctx context.Context, req *message.Request) (*message.Response, error) {
	serviceStub, ok := s.services[req.ServiceName]
	resp := &message.Response{
		RequestID:  req.RequestID,
		Version:    req.Version,
		Compresser: req.Compresser,
		Serializer: req.Serializer,
	}
	if !ok {
		return nil, errors.New("你要调用的服务不存在")
	}
	//反射调用该方法
	respDataBytes, err := serviceStub.invoke(req.MethodName, req.Data)
	resp.Data = respDataBytes
	if err != nil {
		return resp, err
	}
	return resp, nil
}

func (s *Server) Start() error {
	listener, err := net.Listen(s.network, s.addr)
	if err != nil {
		// 比较常见的就是端口被占用
		return err
	}
	for {
		conn, err := listener.Accept()
		if err != nil {
			return err
		}
		go func() {
			if er := s.handleConn(conn); er != nil {
				_ = conn.Close()
			}
		}()
	}
}

func (s *Server) RegisterService(service Service) {
	s.services[service.Name()] = reflectionStub{
		service: service,
		value:   reflect.ValueOf(service),
	}
}

func (s *Server) handleConn(conn net.Conn) error {
	for {
		reqBs, err := ReadMsg(conn)
		if err != nil {
			return err
		}
		// 还原调用信息
		req := message.DecodeReq(reqBs)
		if err != nil {
			return err
		}
		// 反射调用服务端方法
		resp, err := s.Invoke(context.Background(), req)
		if err != nil {
			// 处理业务 error
			resp.Error = []byte(err.Error())
		}
		resp.CalculateHeaderLength()
		resp.CalculateBodyLength()

		_, err = conn.Write(message.EncodeResp(resp))
		if err != nil {
			return err
		}
	}
}

func NewServer(network, addr string) *Server {
	return &Server{
		services: make(map[string]reflectionStub, 16),
		addr:     addr,
		network:  network,
	}
}
